package com.lumlord.common.spring.rpc;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import com.lumlord.common.annotation.ControllerRequestMapping;
import com.lumlord.common.annotation.rpc.Request;
import com.lumlord.common.annotation.rpc.Server;
import com.lumlord.common.base.ApiController;
import com.lumlord.common.domain.Result;
import com.lumlord.common.exception.BusinessException;
import com.lumlord.common.spring.SpringContextUtil;
import com.lumlord.util.RestTemplateUtil;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.core.env.Environment;

import java.lang.reflect.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LumlordNetBridgeProxy extends ApiController implements InvocationHandler {

    private Class<?> interfaceClass;

    private Environment environment;

    private static final String urlPattern="\\{(.+?)\\}";

    private LoadBalancerClient loadBalancerClient;
    private RestTemplateUtil restTemplateUtil;
    public Object bind(Class<?> cls,Environment environment,LoadBalancerClient loadBalancerClient,RestTemplateUtil restTemplateUtil) {
        this.interfaceClass = cls;
        this.environment = environment;
        this.loadBalancerClient = loadBalancerClient;
        this.restTemplateUtil = restTemplateUtil;
        return Proxy.newProxyInstance(cls.getClassLoader(), new Class[] {interfaceClass}, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        LoadBalancerClient loadBalancerClient =  SpringContextUtil.getBean(LoadBalancerClient.class);
        RestTemplateUtil restTemplateUtil =  SpringContextUtil.getBean(RestTemplateUtil.class);

        Server server = this.interfaceClass.getAnnotation(Server.class);
        Request request = method.getAnnotation(Request.class);
        Map<String, Object> parameters = getParameter(method, args);
        parameters.put(ApiController.lumlordVisit,"pc");

        String url = resolveUrl(request,parameters);
        String serverName = getServer(server);
        String path = null;

        if(serverName.indexOf("/")!=-1){ //开发模式设置
            path = String.format("http://"+serverName+"/"+url);
        }else{
            ServiceInstance serviceInstance = loadBalancerClient.choose(serverName);
            if(null == serviceInstance){
                throw  new BusinessException("注册中心找不到对应实例【"+serverName+"】");
            }
            path = String.format("http://%s:%s/%s/"+url, serviceInstance.getHost(), serviceInstance.getPort(),serviceInstance.getServiceId().toLowerCase());
        }

        String methodName =  request.method().name();
        Result result =  invoke(path,methodName.toLowerCase(),parameters,request);

        if(request.requestInfo()){
            log.infoJson(result);
        }

        Class clazzReturnType = getReturnType(method);
        if(200 == result.getCode()){
            Object o = result.getData();
            if(o instanceof  List){
                List list  = (List) o;
                if(clazzReturnType.equals(Map.class)){ //直接返回
                    return list;
                }
                List newList = new ArrayList(list.size());
                for(Object obj : list){
                    if(obj instanceof  Map){
                        Object instance = clazzReturnType.newInstance();
                        if(instance instanceof Map){ //直接返回
                            return list;
                        }
                        Map m = (Map) obj;
                        BeanUtil.fillBeanWithMap(m,instance,true);
                        newList.add(instance);
                    }else{
                        newList.add(Convert.convert(clazzReturnType, o));
                    }
                }
                return  newList;
            }else{
              return Convert.convert(clazzReturnType, o);
           }
        }else{
            throw  new BusinessException("request from ["+path+"] exception like this: "+result.getMsg(),result.getCode());
        }

    }



    private  Class getReturnType(Method method) throws ClassNotFoundException {
        //反射获取返回值
        Class<?> returnType = method.getReturnType();
        if (returnType==List.class){
            Type type = method.getGenericReturnType();
            if (type instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
                //因为list泛型只有一个值 所以直接取0下标
                String typeName = actualTypeArguments[0].getTypeName();
                //真实返回值类型 Class对象
                Class<?> actualType = Class.forName(typeName);
                return  actualType;
            }
        }
        return  returnType;
    }

    private Result invoke(String path, String methodName, Map<String, Object> parameters,Request request) throws BusinessException {
        if(request.requestInfo()){
            log.info("request url : "+path);
            log.infoJson(parameters);
        }
        RestTemplateUtil restTemplateUtil =  SpringContextUtil.getBean(RestTemplateUtil.class);

           if(methodName.equals("get")){
               return restTemplateUtil.get(path);
           }else if(methodName.equals("post")){
               return restTemplateUtil.post(path,parameters);
           }else if(methodName.equals("delete")){
               return restTemplateUtil.delete(path);
           }else if(methodName.equals("put")){
               return restTemplateUtil.put(path,parameters);
           }else {
               throw  new BusinessException("不支持该服务调用需扩展！");
           }
    }


    /**
     * 获取server
     * @param server
     * @return
     */
    private String getServer(Server server) throws  BusinessException{
        String serverInstance =  environment.getProperty("lumlord.server."+server.value());
        if(null == serverInstance){
            throw new BusinessException("【lumlord.server."+server.value()+"】无法找到该服务，请核对！");
        }
        return  serverInstance;
    }



    private Map<String,Object> getParameter(Method method,Object[] values){
        Parameter[] parameters = method.getParameters();
        Map<String,Object> map = new HashMap<String,Object>(parameters.length);
        for(int index=0;index<parameters.length;index++){
           map.put(parameters[index].getName(),values[index]);
        }
        return  map;
    }

    private String resolveUrl (Request request,Map<String, Object> parameters){
        Pattern p=Pattern.compile(urlPattern);
        ControllerRequestMapping c = ApiController.class.getAnnotation(ControllerRequestMapping.class);
        StringBuffer sb = new StringBuffer(c.path()+"/"+request.nameSpace()+"/");
        sb.append(request.path());

        String line = sb.toString();
        String regex = urlPattern;


        Matcher m = p.matcher(line);

        String g;
        while (m.find()) {
            g = m.group();
            g = g.substring(1, g.length() - 1);
            line = m.replaceFirst(parameters.get(g) + "");
            m = p.matcher(line);
        }

        return line;
    }


}